#ifndef _COMPILATION_VISITOR_H_
#define _COMPILATION_VISITOR_H_

#include <compilation/Forward.h>

#include <type_traits>
#include <functional>


namespace compilation
{

namespace detail
{

template <typename _Index_Type,
          typename _Type,
          int _Index,
          template <_Index_Type> class _Template_Struct,
          typename _Return, typename... _Args>
struct recursive_builder
{
  recursive_builder(std::function<_Return(_Type*, _Args...)>* table)
  {
    _Return(_Template_Struct<_Index_Type(_Index)>::*function)(_Type*,_Args...) =
        &_Template_Struct<_Index_Type(_Index)>::operator ();

    table[_Index] =
        [function](_Type* input, _Args... args)
    {
      return (reinterpret_cast<_Template_Struct<_Index_Type(_Index)>*>(0)
        ->*function)(input, args...);
    };

    recursive_builder<_Index_Type, _Type, _Index-1, _Template_Struct, _Return, _Args...>
        builder(table);
  }
};

template <typename _Index_Type,
          typename _Type,
          template <_Index_Type> class _Template_Struct,
          typename _Return, typename... _Args>
struct recursive_builder<
    _Index_Type, _Type, -1, _Template_Struct, _Return, _Args...>
{ recursive_builder(std::function<_Return(_Type*, _Args...)>*) {} };

} // detail

template <typename _Type, typename _Index_Type,
          int _Index_Max,
          typename _Return, typename... _Args>
class Visitor
{
public:

  typedef _Return return_type;
  typedef _Type type;
  typedef _Index_Type index_type;
  typedef std::function<return_type(type*, _Args...)> func_type;

  template <template <index_type>
            class _Template_Struct>
  static Visitor
  build_visitor(void)
  {
    Visitor visitor;

    detail::recursive_builder<
        _Index_Type, _Type, _Index_Max - 1, _Template_Struct, _Return, _Args...>
        builder(visitor._vtable);

    return visitor;
  }

  template <index_type _Tp>
  void set(func_type const& fun)
  { _vtable[_Tp] = fun; }

  return_type operator ()(type* input, _Args... args) const
  { return _vtable[input->type()](input, args...); }

protected:

  func_type _vtable[_Index_Max];

  Visitor(void) {}
};

} // compilation

#endif // _COMPILATION_VISITOR_H_
